package com.xqboss.apps.service.box;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xqboss.apps.constant.Config;
import com.xqboss.apps.constant.LockKey;
import com.xqboss.apps.constant.RedisKey;
import com.xqboss.apps.domain.box.Box;
import com.xqboss.apps.domain.box.BoxNft;
import com.xqboss.apps.domain.box.BoxPriorityBuy;
import com.xqboss.apps.domain.box.BoxSimulatedRecord;
import com.xqboss.apps.domain.nft.Nft;
import com.xqboss.apps.domain.sys.SysHistoryActivity;
import com.xqboss.apps.dto.PageDto;
import com.xqboss.apps.dto.box.BindBoxDto;
import com.xqboss.apps.dto.box.BoxDto;
import com.xqboss.apps.dto.box.BoxSaleableDto;
import com.xqboss.apps.dto.market.MarketDto;
import com.xqboss.apps.enums.mq.AppMqDelayEnum;
import com.xqboss.apps.enums.nft.NftTypeEnum;
import com.xqboss.apps.enums.nft.ResellEnum;
import com.xqboss.apps.enums.sys.SaleableEnum;
import com.xqboss.apps.mapper.box.BoxMapper;
import com.xqboss.apps.mapper.box.BoxNftMapper;
import com.xqboss.apps.mapper.nft.NftMapper;
import com.xqboss.apps.service.market.MarketService;
import com.xqboss.apps.service.msg.PushService;
import com.xqboss.apps.service.order.OrderService;
import com.xqboss.apps.service.sys.SysHistoryActivityService;
import com.xqboss.apps.vo.box.*;
import com.xqboss.apps.vo.market.MarketListVo;
import com.xqboss.common.constant.HttpStatus;
import com.xqboss.common.core.domain.R;
import com.xqboss.common.core.domain.model.LoginUser;
import com.xqboss.common.core.mq.MqSender;
import com.xqboss.common.core.redis.RedisCache;
import com.xqboss.common.core.redis.lock.LockValue;
import com.xqboss.common.core.redis.lock.RedisLock;
import com.xqboss.common.exception.ServiceException;
import com.xqboss.common.utils.LocalDateUtils;
import com.xqboss.common.utils.SecurityUtils;
import com.xqboss.common.utils.StringUtils;
import com.xqboss.system.service.ISysConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import static com.xqboss.common.utils.AsyncHandlerUtil.run;

/**
 * @author Administrator
 */
@Service
public class BoxService extends ServiceImpl<BoxMapper, Box> {
    @Autowired
    private BoxNftService boxNftService;
    @Resource
    private NftMapper nftMapper;
    @Resource
    private BoxNftMapper boxNftMapper;
    @Resource
    private BoxMapper boxMapper;
    @Resource
    private BoxSimulatedRecordService boxSimulatedRecordService;
    @Resource
    private SysHistoryActivityService sysHistoryActivityService;
    @Autowired
    private ISysConfigService sysConfigService;

    @Autowired
    @Lazy
    private OrderService orderService;

    @Autowired
    private RedisCache redisCache;

    @Lazy
    @Autowired
    private MqSender mqSender;

    @Autowired
    private PushService pushService;

    @Autowired
    private BoxPriorityBuyService boxPriorityBuyService;

    @Transactional(rollbackFor = Exception.class)
    public void insert(BoxDto dto) {
        if (ObjUtil.isNull(dto.getPrice()) || dto.getPrice().compareTo(BigDecimal.ZERO) < 0
                || CollUtil.isEmpty(dto.getBindNft())) {
            throw new ServiceException("参数错误");
        }
        if (dto.getIsResell() == ResellEnum.OPEN) {
            if (Objects.isNull(dto.getLowestPrice()) || Objects.isNull(dto.getMaxPrice())) {
                throw new ServiceException("价格区间不能为空");
            }
        }
        if (Objects.nonNull(dto.getLowestPrice()) && Objects.nonNull(dto.getMaxPrice())) {
            if (dto.getLowestPrice().compareTo(dto.getMaxPrice()) > 0) {
                throw new ServiceException("最低价小于或等于最高价");
            }
        }
        if (dto.getOnceLimitStock() > dto.getLimitStock()) {
            throw new ServiceException("箱子单次限购数量不能大于限购数量");
        }
        List<BindBoxDto> nfts = dto.getBindNft();
        //箱子库存总数等于多个典藏或装备添加数量之和，剩余库存等于多个典藏或装备剩余库存之和
        int nftCount = nfts.stream().map(BindBoxDto::getBindCount).collect(Collectors.toList()).stream().mapToInt(Integer::intValue).sum();
        Box box = BeanUtil.copyProperties(dto, Box.class);
        box.setId(IdUtil.getSnowflakeNextId());
        box.setSaleable(0);
        box.setStock(nftCount);
        box.setTotalStock(nftCount);
        //箱子没有开始发售时间，默认当前时间
        if (ObjUtil.isNull(box.getPublishTimeStart())) {
            box.setPublishTimeStart(LocalDateUtils.getCurrentLocalDateTime());
        }
        if (ObjUtil.isNotNull(box.getPublishTimeEnd())) {
            boolean b = LocalDateUtils.compareDateTimeGt(box.getPublishTimeEnd(), box.getPublishTimeStart());
            if (!b) {
                throw new ServiceException("结束时间需大于开始时间");
            }
        }
        //添加箱子
        save(box);

        nfts.forEach(item -> {
            Nft nft = nftMapper.selectById(item.getNftId());
            if (Objects.isNull(nft)) {
                throw new ServiceException("藏品（" + item.getNftId() + "）不存在");
            }
            if (nft.getStock() < item.getBindCount()) {
                throw new ServiceException("绑定藏品（" + item.getNftId() + "）库存不足");
            }
            //扣减对应典藏的库存
            int res = nftMapper.abatementCount(item.getNftId(), item.getBindCount());
            if (res != 1) {
                throw new ServiceException("扣减藏品（" + item.getNftId() + "）库存失败");
            }
            //箱子与nft绑定关系
            BoxNft boxNft = new BoxNft();
            boxNft.setBoxId(box.getId());
            boxNft.setNftId(item.getNftId());
            boxNft.setStock(item.getBindCount());
            boolean b = boxNftService.save(boxNft);
            if (!b) {
                throw new ServiceException("箱子绑定藏品关系失败");
            }
        });
    }

    @Transactional(rollbackFor = Exception.class)
    public void update(BoxDto dto) {
        Box res = getById(dto.getId());
        if (ObjUtil.isNull(res)) {
            throw new ServiceException("箱子不存在");
        }
        if (ObjUtil.isNotNull(dto.getOnceLimitStock()) && ObjUtil.isNotNull(dto.getLimitStock())
                && dto.getOnceLimitStock() > dto.getLimitStock()) {
            throw new ServiceException("单次限购数量不能大于限购数量");
        }
        if (ObjUtil.isNotNull(dto.getPublishTimeEnd())) {
            boolean b = LocalDateUtils.compareDateTimeGt(dto.getPublishTimeEnd(), dto.getPublishTimeStart());
            if (!b) {
                throw new ServiceException("结束时间需大于开始时间");
            }
        }
        if (dto.getIsResell() == ResellEnum.OPEN) {
            if (Objects.isNull(dto.getLowestPrice()) || Objects.isNull(dto.getMaxPrice())) {
                throw new ServiceException("价格区间不能为空");
            }
        }
        if (Objects.nonNull(dto.getLowestPrice()) && Objects.nonNull(dto.getMaxPrice())) {
            if (dto.getLowestPrice().compareTo(dto.getMaxPrice()) > 0) {
                throw new ServiceException("最低价小于或等于最高价");
            }
        }
        Box box = BeanUtil.copyProperties(dto, Box.class,  "saleable");

        //编辑箱子的藏品
        if (CollUtil.isNotEmpty(dto.getBindNft())) {
            //编辑箱子的藏品时需要先解绑之前的藏品
            long boxNftCount = boxNftService.count(new LambdaQueryWrapper<BoxNft>().eq(BoxNft::getBoxId, box.getId()));
            if (boxNftCount > 0) {
                throw new ServiceException("箱子已绑定藏品");
            }
            List<BindBoxDto> nfts = dto.getBindNft();
            //箱子库存总数等于多个典藏或装备添加数量之和，剩余库存等于多个典藏或装备剩余库存之和
            int nftCount = nfts.stream().map(BindBoxDto::getBindCount).collect(Collectors.toList()).stream().mapToInt(Integer::intValue).sum();
            nfts.forEach(item -> {
                Nft nft = nftMapper.selectNftById(item.getNftId());
                if (Objects.isNull(nft)) {
                    throw new ServiceException("藏品（" + item.getNftId() + "）不存在");
                }
                if (nft.getStock() < item.getBindCount()) {
                    throw new ServiceException("绑定藏品（" + item.getNftId() + "）库存不足");
                }
                //扣减对应典藏的库存
                int result = nftMapper.abatementCount(item.getNftId(), item.getBindCount());
                if (result != 1) {
                    throw new ServiceException("扣减藏品（" + item.getNftId() + "）库存失败");
                }
                //箱子与nft绑定关系
                BoxNft boxNftre = new BoxNft();
                boxNftre.setBoxId(box.getId());
                boxNftre.setNftId(item.getNftId());
                boxNftre.setStock(item.getBindCount());
                boolean b = boxNftService.save(boxNftre);
                if (!b) {
                    throw new ServiceException("箱子绑定藏品关系失败");
                }
            });

            box.setStock(nftCount);
            box.setTotalStock(nftCount);
        }
        //编辑箱子
        boolean b = baseMapper.updateBoxById(box);
        if (!b) {
            throw new ServiceException("更新箱子失败");
        }

        redisCache.deleteObject(RedisKey.KEY_CACHE_BOX + box.getId());

        //关闭寄售的同时，自动下架市场所有专卖中的宝盒
        if (dto.getIsResell() == ResellEnum.SHUT) {
            String result = String.valueOf(res.getId()) + ',' + NftTypeEnum.BOX.getValue();
            mqSender.sendDelay(AppMqDelayEnum.MARKET_AUTO_OFF_SHELF, result, 300L);
//            marketService.offShelfAuto(res.getId(), NftTypeEnum.BOX.getValue());
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void untie(Long boxId, Long userId) {
        Box box = getById(boxId);
        if (ObjUtil.isNull(box)) {
            throw new ServiceException("箱子不存在，无法解绑");
        }
        if (box.getSaleable() != 0) {
            throw new ServiceException("上架状态不能解绑");
        }
        if (box.getLockStock() > 0) {
            throw new ServiceException("还有待支付订单，暂时无法解绑");
        }
        List<BoxNft> boxNfts = boxNftService.list(new LambdaQueryWrapper<BoxNft>().eq(BoxNft::getBoxId, boxId));
        if (CollUtil.isEmpty(boxNfts)) {
            throw new ServiceException("箱子绑定的藏品不存在");
        }
        //解绑箱子中的典藏返回库存
        boxNfts.forEach(item -> {
            int res = nftMapper.addCount(item.getNftId(), item.getStock(), 2);
            if (res != 1) {
                throw new ServiceException("nft库存返还失败");
            }
            boxNftMapper.deleteById(item.getId());
        });

        //清零箱子的库存
        LambdaUpdateWrapper<Box> bowq = new LambdaUpdateWrapper<>();
        bowq.set(Box::getStock, 0);
        bowq.set(Box::getTotalStock, 0);
        bowq.set(Box::getSimulatedSales, 0);
        bowq.eq(Box::getId, boxId);
        boolean b = update(bowq);
        if (!b) {
            throw new ServiceException("解绑箱子库存数失败");
        }

        LambdaQueryWrapper<BoxSimulatedRecord> wp = new LambdaQueryWrapper<>();
        wp.eq(BoxSimulatedRecord::getBoxId, boxId);
        List<BoxSimulatedRecord> one = boxSimulatedRecordService.list(wp);
        for (BoxSimulatedRecord item : one) {
            //新增一条返还扣减虚拟销量
            BoxSimulatedRecord record = new BoxSimulatedRecord();
            record.setAmount(-item.getAmount());
            record.setBoxId(box.getId());
            record.setUserId(SecurityUtils.getUserId());
            record.setUserName(SecurityUtils.getUsername());
            //添加虚拟销量记录
            boolean result = boxSimulatedRecordService.save(record);
            if (!result) {
                throw new ServiceException("返还扣减虚拟销量失败");
            }
            //虚拟销量清除
            boolean remove = boxSimulatedRecordService.remove(wp);
            if (!remove) {
                throw new ServiceException("解绑箱子虚拟销量数失败");
            }
        }
    }

    public List<BoxVo> selectListByDto(BoxDto dto) {
        List<BoxVo> voList = boxMapper.selectListByDto(dto);
        LocalDateTime currentTime = LocalDateUtils.parseToLocalDateTime(new Date());
        voList.forEach(item -> {
            if (LocalDateUtils.compareDateTime(item.getPublishTimeStart(), currentTime)) {
                item.setSaleStatus(0);
            } else if (LocalDateUtils.compareDateTime(currentTime, item.getPublishTimeStart())) {
                item.setSaleStatus(1);
            }
            if (ObjUtil.isNotNull(item.getPublishTimeEnd()) && LocalDateUtils.compareDateTime(currentTime, item.getPublishTimeEnd())) {
                item.setSaleStatus(2);
            }
//            item.setRemainderStock(Math.max(item.getStock() - item.getSales(), 0));
        });
        return voList;
    }

    /**
     * 锁定库存
     *
     * @param boxId
     * @param quantity
     */
    public void lockStock(@LockValue Long boxId, Integer quantity) {
        if (Objects.isNull(quantity) || quantity <= 0) {
            throw new ServiceException("操作数量错误！");
        }
        // 库存
        boolean update = update(new LambdaUpdateWrapper<Box>()
                .setSql("stock=stock-" + quantity)
                .setSql("lock_stock=lock_stock+" + quantity)
                .eq(Box::getId, boxId)
                .ge(Box::getStock, quantity)
        );
        if (!update) {
            Box box = getById(boxId);
            int stock = box.getStock();
            if (stock < quantity) {
                // 如果剩余库存
                if (stock > 0) {
                    throw new ServiceException(StringUtils.format("库存仅剩{}份", stock), HttpStatus.OUT_OF_STOCK);
                } else {
                    // 判断是否已售罄
                    if (box.getLockStock() <= 0) {
                        redisCache.deleteObject(RedisKey.KEY_CACHE_BOX + box.getId());
                        throw new ServiceException("已售罄！");
                    } else {
                        // 如果已售数量 等于 优先购已售数量， 则返回外显库存数
                        if(box.getSales() <= box.getPrioritySales()){
                            throw new ServiceException(StringUtils.format("暂无库存，剩余{}件商品待支付！", box.getDisplayStock() - box.getDisplayPrioritySales()));
                        }else{
                            throw new ServiceException(StringUtils.format("暂无库存，剩余{}件商品待支付！", box.getLockStock()));
                        }
                    }
                }
            }
            throw new ServiceException("库存不足！");
        }
    }

    /**
     * 解锁库存
     *
     * @param boxId    箱子ID
     * @param quantity
     */
    public void unlockStock(@LockValue Long boxId, Integer quantity) {
        if (Objects.isNull(quantity) || quantity <= 0) {
            throw new ServiceException("操作数量错误！");
        }
        // 库存
        boolean update = update(new LambdaUpdateWrapper<Box>()
                .setSql("stock=stock+" + quantity)
                .setSql("lock_stock=lock_stock-" + quantity)
                .eq(Box::getId, boxId)
        );
        if (!update) {
            throw new ServiceException("解锁库存失败！");
        }
    }

    /**
     * 扣除库存
     *
     * @param boxId
     * @param quantity
     */
    @RedisLock(lockKey = LockKey.LOCK_BOX_STOCK)
    @Transactional(rollbackFor = Exception.class)
    public void takeStock(@LockValue Long boxId, Integer quantity) {
        if (Objects.isNull(quantity) || quantity <= 0) {
            throw new ServiceException("操作数量错误！");
        }
        boolean update = update(new LambdaUpdateWrapper<Box>()
                .setSql("stock=stock-" + quantity)
                .eq(Box::getId, boxId)
                .ge(Box::getStock, quantity)
        );
        if (!update) {
            throw new ServiceException("库存不足！");
        }
    }


    /**
     * 扣除锁定库存
     *
     * @param boxId
     * @param quantity
     * @return
     */
    @RedisLock(lockKey = LockKey.LOCK_BOX_STOCK, waitTime = 30)
    @Transactional(rollbackFor = Exception.class)
    public Map<BoxNft, Integer> takeLockStore(@LockValue Long boxId, Integer quantity, Set<Long> nftIdList) {
        if (Objects.isNull(quantity) || quantity <= 0) {
            throw new ServiceException("操作数量错误！");
        }
        boolean update = update(new LambdaUpdateWrapper<Box>()
                .setSql("lock_stock=lock_stock-" + quantity)
                .setSql("sales=sales+" + quantity)
                .eq(Box::getId, boxId)
        );
        if (!update) {
            throw new ServiceException("扣除库存失败！");
        }
        return boxNftService.takeStock(boxId, quantity, nftIdList);
    }


    public List<BoxMallVo> selectAppListByDto(PageDto dto) {
        List<BoxMallVo> voList = boxMapper.selectAppListByDto(dto);
        LocalDateTime currentTime = LocalDateUtils.parseToLocalDateTime(new Date());
        voList.forEach(item -> {
            if (LocalDateUtils.compareDateTime(item.getPublishTimeStart(), currentTime)) {
                item.setSaleStatus(0);
            } else if (LocalDateUtils.compareDateTime(currentTime, item.getPublishTimeStart())) {
                item.setSaleStatus(1);
            }
            if (ObjUtil.isNotNull(item.getPublishTimeEnd()) && LocalDateUtils.compareDateTime(currentTime, item.getPublishTimeEnd())) {
                item.setSaleStatus(2);
            }
            //倒计时
            long countdown = LocalDateTimeUtil.between(LocalDateTime.now(), item.getPublishTimeStart(), ChronoUnit.SECONDS);
            if (countdown < 0) {
                countdown = 0;
            }
            item.setSaleCountdown((int) countdown);
            item.setSoldOut(item.getStock() <= 0 && item.getLockStock() <= 0);
        });
        return voList;
    }

    public R<BoxPayInfoVo> payInfo(BoxDto dto) {
        if (ObjUtil.isNull(dto) || ObjUtil.isNull(dto.getId())) {
            return R.fail("参数错误！");
        }
        BoxInfoVo vo = boxMapper.payInfo(dto);
        if (ObjUtil.isNull(vo)) {
            return R.fail("箱子已下架");
        }
        LoginUser loginUser = SecurityUtils.getLoginUserOrNull();
        if(loginUser != null){
            BoxPriorityBuy boxPriorityBuy = boxPriorityBuyService.getBoxPriorityBuy(loginUser.getUserId(), vo.getId());
            if(boxPriorityBuy != null){
                // 判断当前时间在优先购结束之前，则展示优先购时间
                LocalDateTime now = LocalDateTime.now();
                if(now.isBefore(boxPriorityBuy.getPublishTimeEnd())){
                    vo.setPublishTimeStart(boxPriorityBuy.getPublishTimeStart());
                    vo.setPublishTimeEnd(boxPriorityBuy.getPublishTimeEnd());
                    vo.setLimitStock(boxPriorityBuy.getLimitStock());
                }
            }
        }

        long countdown = LocalDateTimeUtil.between(LocalDateTime.now(), vo.getPublishTimeStart(), ChronoUnit.SECONDS);
        if (countdown < 0) {
            countdown = 0;
        }
        vo.setSaleCountdown((int) countdown);
        vo.setBuyKnow(sysConfigService.getValue(Config.Sys.Nft.购买须知));
        vo.setCopyrightNotice(sysConfigService.getValue(Config.Sys.Nft.版权声明));
        vo.setSoldOut(vo.getStock() <= 0 && vo.getLockStock() <= 0);

        BoxPayInfoVo res = BeanUtil.copyProperties(vo, BoxPayInfoVo.class);
        return R.ok(res);
    }

    /**
     * 获取用户当前剩余可购买数量
     *
     * @param userId
     * @param boxId
     */
    public Integer getUserRemainingLimit(Long userId, Long boxId) {
        BoxPriorityBuy boxPriorityBuy = boxPriorityBuyService.getBoxPriorityBuy(userId, boxId);
        if(boxPriorityBuy != null){
            if(LocalDateTime.now().isBefore(boxPriorityBuy.getPublishTimeEnd())){
                // 优先购时段内，取优先购相关配置处理
                int spuOrderCount = orderService.getSpuOrderCount(userId, boxId, true);
                int limit = boxPriorityBuy.getLimitStock() - spuOrderCount;
                if (limit < 0) {
                    return 0;
                }
                return limit;
            }
        }

        Box box = getById(boxId);
        if (Objects.nonNull(box)) {
            if (Objects.nonNull(box.getLimitStock())) {
                int spuOrderCount = orderService.getSpuOrderCount(userId, boxId, false);
                int limit = box.getLimitStock() - spuOrderCount;
                if (limit < 0) {
                    return 0;
                }
                return limit;
            }
        }
        return null;
    }

    /**
     * 获取箱子分类信息
     *
     * @param spuId
     * @return
     */
    public BoxInfoVo getCategoryInfo(Long boxId) {
        return getBaseMapper().getCategoryInfo(boxId);
    }

    public void changeSaleable(BoxSaleableDto dto) {
        Box box = getOne(new LambdaQueryWrapper<Box>()
                .select(Box::getId, Box::getName, Box::getActivityImg, Box::getPublishTimeStart)
                .eq(Box::getId, dto.getId()));
        if (Objects.isNull(box)) {
            throw new ServiceException("箱子不存在");
        }
        BeanUtil.copyProperties(dto, box);
        //箱子上架校验藏品
        if (ObjUtil.equal(dto.getSaleable().getValue(), SaleableEnum.SEQUENCE.getValue())) {
            long boxNftCount = boxNftService.count(new LambdaQueryWrapper<BoxNft>().eq(BoxNft::getBoxId, box.getId()));
            if (boxNftCount <= 0) {
                throw new ServiceException("箱子未绑定藏品无法上架");
            }
            //新增历史活动
            LambdaQueryWrapper<SysHistoryActivity> hiswp = new LambdaQueryWrapper<>();
            hiswp.eq(SysHistoryActivity::getBoxId, dto.getId());
            SysHistoryActivity one = sysHistoryActivityService.getOne(hiswp);
            if (ObjUtil.isNull(one)) {
                SysHistoryActivity activity = new SysHistoryActivity();
                activity.setTitle("首发-" + box.getName());
                activity.setImg(box.getActivityImg());
                activity.setBoxId(dto.getId());
                activity.setType(1);
                sysHistoryActivityService.save(activity);
            }

            // 如果是定时发售，则发送定时消息
            if(Objects.nonNull(box.getPublishTimeStart())){
                // 取消以前的开售延迟消息
                mqSender.cancelDelay(AppMqDelayEnum.BOX_SALE_START, box.getId());
                int between = (int) LocalDateTimeUtil.between(LocalDateTime.now(), box.getPublishTimeStart(), ChronoUnit.SECONDS);
                if(between > 10){
                    // 推送上架消息
                    pushService.pushBoxSaleableMsg(box.getName(), box.getId());
                    mqSender.sendDelay(AppMqDelayEnum.BOX_SALE_START, box.getId(), box.getPublishTimeStart());
                }else{
                    pushService.pushBoxSaleMsg(box.getName(), box.getId());
                }
            }
        }
        boolean b = updateById(box);
        if (!b) {
            throw new ServiceException("编辑箱子失败");
        }
        redisCache.deleteObject(RedisKey.KEY_CACHE_BOX + box.getId());
    }

    /**
     * 获取箱子
     * @param boxId
     * @return
     */
    public Box getBoxById(Long boxId){
        Box box = getById(boxId);
        if(box == null){
            throw new ServiceException("箱子不存在！");
        }
        return box;
    }

    /**
     * 获取箱子转卖状态
     * @param boxId
     * @return
     */
    public Boolean isBoxResell(Long boxId) {
        Box box = getOne(new LambdaQueryWrapper<Box>()
                .select(Box::getIsResell)
                .eq(Box::getId, boxId));
        if(Objects.nonNull(box)){
            return box.getIsResell() == ResellEnum.OPEN;
        }
        return false;
    }

    public List<MarketListVo> getBoxList(MarketDto dto) {
        List<MarketListVo> boxList = this.baseMapper.getBoxList(dto);
        if (CollUtil.isNotEmpty(boxList)) {
            ArrayList<CompletableFuture<Void>> futures = new ArrayList<>(boxList.size()*3);
            for (MarketListVo box : boxList) {
                //市场在卖专卖中最低价
                CompletableFuture<Void> a = run(()->box.getMarketVo().setLowestPrice(baseMapper.selectMarketByBoxIdMin(box.getId())));
                //市场已卖最高价
                CompletableFuture<Void> b = run(()->box.getMarketVo().setMaxPrice(baseMapper.selectMarketByBoxIdMax(box.getId())));
                //市场在售数量
                CompletableFuture<Void> c = run(()->box.getMarketVo().setSellCount(baseMapper.selectMarketByBoxIdSell(box.getId())));

                futures.addAll(CollUtil.toList(a,b,c));
            }
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
        }
        return boxList;
    }

    public IPage<MarketListVo> getBoxListPlus(MarketDto<MarketListVo> dto) {
        IPage<MarketListVo> iPage = this.baseMapper.getBoxListPlus(dto.newPage(),dto);
        if (ObjUtil.isNotEmpty(iPage) && CollUtil.isNotEmpty(iPage.getRecords())) {
            ArrayList<CompletableFuture<Void>> futures = new ArrayList<>(iPage.getRecords().size()*3);
            for (MarketListVo box : iPage.getRecords()) {
                //市场在卖专卖中最低价
                CompletableFuture<Void> a = run(()->box.getMarketVo().setLowestPrice(baseMapper.selectMarketByBoxIdMin(box.getId())));
                //市场已卖最高价
                CompletableFuture<Void> b = run(()->box.getMarketVo().setMaxPrice(baseMapper.selectMarketByBoxIdMax(box.getId())));
                //市场在售数量
                CompletableFuture<Void> c = run(()->box.getMarketVo().setSellCount(baseMapper.selectMarketByBoxIdSell(box.getId())));

                futures.addAll(CollUtil.toList(a,b,c));
            }
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
        }
        return iPage;
    }
}
